#include "mysql_query.h"
#include "libzqutil/log.h"

#ifdef MYSQL_DEBUG
#define LOG_MYSQL(sql) LOGT(sql)
#else
#define LOG_MYSQL(sql) (sql)
#endif // MYSQL_DEBUG

namespace libzq
{
	namespace mysql
	{
		const static char* ordrby_op[] = { "ASC", "DESC" };

		void ColumData::set_result(const char * field, const string && res)
		{
			_data_map[field] = res;
		}

		const string & ColumData::operator[](const char * field) const
		{
			auto itor = _data_map.find(field);
			if (itor == _data_map.end())
			{
				throw NoFieldError(field);
			}
			return itor->second;
		}

		const string & ColumData::operator[](const string & field) const
		{
			return this->operator [](field.c_str());
		}

		const vector<string> ColumData::keys() const
		{
			vector<string> keys_;
			map<string, string>::const_iterator itor = _data_map.begin();
			for (; itor != _data_map.end(); ++itor)
			{
				keys_.push_back(itor->first);
			}
			return std::move(keys_);
		}

		FieldFilter::FieldFilter(const Field& field, Operator op, const FieldValue & val) :
			_field(field),
			_op(op),
			_value(val)
		{
		}

		string FieldFilter::to_string(const string& table) const
		{
			return std::move(format("%s %s %s", _field.to_string(table), operator_[_op], _value.to_string()));
		}


		MultiFilter::MultiFilter(const char* op) :
			_op(op)
		{
		}

		MultiFilter::MultiFilter(std::initializer_list<shared_ptr<FilterImpl>> conds)
		{
			for (auto itor : conds)
			{
				_cond_ptr_vec.push_back(itor);
			}
		}

		void MultiFilter::add(shared_ptr<FilterImpl> cond)
		{
			_cond_ptr_vec.push_back(cond);
		}

		void MultiFilter::get_cond_str_vec(const string& table, vector<string>& str_vec) const
		{
			for (auto cond : _cond_ptr_vec)
			{
				str_vec.emplace_back(cond->to_string(table.c_str()));
			}
		}

		string MultiFilter::to_string(const string & table) const
		{
			vector<string> cond_str_vec;
			get_cond_str_vec(table, cond_str_vec);
			return std::move(format("(%s)", join(cond_str_vec, _op)));
		}

		Query::Query(const char * table) :
			_table(table),
			_select_for_update(false)
		{
			_limit[0] = 0;
			_limit[1] = 0;
		}

		Query & Query::filter(shared_ptr<FilterImpl> cond)
		{
			_filter = cond;
			return *this;
		}
		Query & Query::orderby(const string && field)
		{
			bool desc = field[0] == '-';
			_order_vec.emplace_back(OrderBy{ Field{ desc ? string(field.c_str() + 1) : std::move(field)}, desc ? DESC : ASC });
			return *this;
		}

		Query & Query::groupby(Field && field)
		{
			if (field._table.empty()) field._table = _table;
			_group_vec.emplace_back(field);
			return *this;
		}

		Query & Query::groupby(std::string && field)
		{
			return groupby(Field(field));
		}

		Query & Query::left_join(Join && join)
		{
			join._join_rule = Join::__LEFT_JOIN__;
			merge_join(join);
			return *this;
		}

		Query& Query::right_join(Join&& join)
		{
			join._join_rule = Join::__RIGHT_JOIN__;
			merge_join(join);
			return *this;
		}

		Query & Query::inner_join(Join && join)
		{
			join._join_rule = Join::__INNER_JOIN__;
			merge_join(join);
			return *this;
		}

		Query & Query::limit(int32_t num)
		{
			_limit[0] = 0;
			_limit[1] = num;
			return *this;
		}

		Query & Query::limit(int32_t offset, int32_t num)
		{
			_limit[0] = offset;
			_limit[1] = num;
			return *this;
		}

		Query & Query::for_update()
		{
			_select_for_update = true;
			return *this;
		}

		string Query::to_string() const
		{
			string table = _table;
			if (!_join_vec.empty())
			{
				table = format("`%s` %s", table, Query::conds_to_string<Join>(_join_vec, " "));
			}
			else
			{
				table = format("`%s`", table);
			}
			string query = format("SELECT %s FROM %s WHERE %s", Query::conds_to_string<Field>(_fields, ", "), table, _filter ? _filter->to_string(_table) : "1");
			if (!_group_vec.empty())
			{
				query = format("%s GROUP BY %s", query, Query::conds_to_string<Field>(_group_vec, ", "));
			}
			if (!_order_vec.empty())
			{
				query = format("%s ORDER BY %s", query, Query::conds_to_string<OrderBy>(_order_vec, ", "));
			}
			if (_limit[1] != 0)
			{
				query = format("%s LIMIT %d, %d", query, _limit[0], _limit[1]);
			}
			if (_select_for_update)
			{
				query = format("%s FOR UPDATE", query);
			}
			LOG_MYSQL(query);
			return std::move(query);
		}

		const ColumData & Query::one()
		{
			if (_result.empty())
				throw NoResultError{};
			return _result[0];
		}

		const vector<ColumData>& Query::all_result()
		{
			return _result;
		}

		bool Query::empty() const
		{
			return _result.empty();
		}

		void Query::merge_join(const Join & join)
		{
			for (auto itor : join._field_vec)
			{
				_fields.emplace_back(itor);
			}
			_join_vec.push_back(join);
		}

		Join Query::on(shared_ptr<FilterImpl> cond) const
		{
			return std::move(Join{ _table, nullptr, _fields, cond });
		}

		TableFilter::TableFilter(const Field & field_from, Operator op, const Field & field_to) :
			_table_field_from(field_from),
			_op(op),
			_table_field_to(field_to)
		{
		}

		string TableFilter::to_string(const string & table) const
		{
			return std::move(format("%s %s %s", _table_field_from.to_string(), operator_[_op], _table_field_to.to_string()));
		}

		string OrderBy::to_string(const string & table) const
		{
			return std::move(format("%s %s", _field.to_string(table), ordrby_op[_op]));
		}

		string Join::to_string() const
		{
			return format("%s JOIN `%s` ON (%s)", _join_rule, _table, _filter->to_string());
		}

		Insert::Insert(const char * table) :
			_table(table)
		{
		}

		string Insert::to_string()
		{
			vector<string> fields_vec;
			vector<string> values_vec;

			for (auto field : _fields)
			{
				FieldFilter* field_ = static_cast<FieldFilter*>(field.get());
				fields_vec.push_back(field_->_field._field);
				values_vec.emplace_back(field_->_value.to_string());
			}

			string query = format("INSERT INTO `%s`(%s) VALUES (%s)", _table, join(fields_vec, ", "), join(values_vec, ", "));
			LOG_MYSQL(query);
			return std::move(query);
		}

		Update::Update(const char * table) :
			_table(table)
		{
		}

		Update & Update::filter(shared_ptr<FilterImpl> filter)
		{
			_filter = filter;
			return *this;
		}

		string Update::to_string()
		{
			vector<string> field_vec;
			for (auto field : _fields)
			{
				field_vec.emplace_back(field->to_string());
			}
			string query = format("UPDATE `%s` SET %s WHERE %s", _table, join(field_vec, ", "), _filter->to_string(_table));
			LOG_MYSQL(query);
			return std::move(query);
		}

		Delete::Delete(const char * table) :
			_table(table)
		{
		}

		Delete & Delete::filter(shared_ptr<FilterImpl> filter)
		{
			_filter = filter;
			return *this;
		}

		string Delete::to_string()
		{
			string query = format("DELETE FROM `%s` WHERE %s", _table, _filter->to_string(_table));
			LOG_MYSQL(query);
			return std::move(query);
		}

		Exist::Exist(const char * table) :
			_table(table)
		{
		}
		Exist & Exist::filter(shared_ptr<FilterImpl> filter)
		{
			_filter = filter;
			return *this;
		}
		string Exist::to_string()
		{
			string query = format("SELECT 1 FROM `%s` WHERE %s LIMIT 1", _table, _filter->to_string(_table));
			LOG_MYSQL(query);
			return std::move(query);
		}

		RawFilter::RawFilter(const char * filter) :
			_filter(filter)
		{

		}
		RawFilter::RawFilter(const string & filter) :
			_filter(filter)
		{

		}
		RawFilter::RawFilter(const string && filter) :
			_filter(filter)
		{

		}
		string RawFilter::to_string(const string & table) const
		{
			return _filter;
		}
	}
}
